/**
 * Copyright (C) 2018 - 2020 https://github.com/LeeYan9
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.bifrost.service.impl;

import com.bifrost.common.util.HttpRequestUtils;
import com.bifrost.entity.*;
import com.bifrost.exception.KnownRuntimeException;
import com.bifrost.model.PlanCommitModel;
import com.bifrost.model.ro.PlanItemModel;
import com.bifrost.model.ro.PlanModel;
import com.bifrost.model.vo.PageVO;
import com.bifrost.repository.*;
import com.bifrost.service.PlanService;
import com.bifrost.service.SkuMediaMapService;
import com.bifrost.service.StoreService;
import com.bifrost.util.LocalDateUtils;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.json.HTTP;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Lee
 * Create Class on 2018/8/13
 * @com.bifrost.service
 * @wechat-mall
 */
@Service
@Slf4j
public class PlanServiceImpl implements PlanService {

    @Autowired
    StoreService storeService;
    @Autowired
    PlanItemRepository planItemRepository;
    @Autowired
    PlanRepository planRepository;
    @Autowired
    PlanCustomerRepository planCustomerRepository;
    @Autowired
    CustomerRepository customerRepository;
    @Autowired
    SkuMediaMapService skuMediaMapService;
    @Autowired
    ProductSkuRepository skuRepository;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(PlanModel planModel, MerchantStore store, User user) {
        Plan plan = new Plan();
        if(plan.getPlanId()!=null){
            plan = planRepository.findOne(plan.getPlanId());
        }else {
            plan.setCreatedTimestamp(new Date());
            plan.setStoreId(store.getMerchantId());
        }
        //保存申领活动主体
        BeanUtils.copyProperties(planModel,plan);
        plan.setUpdatedTimestamp(new Date());
        plan.setUpdatedBy(user.getAdminName());
        Assert.notNull(plan = planRepository.save(plan),"保存申领活动");

        //保存商品申领活动
        List<PlanItemModel> planItemModels = planModel.getPlanItemModels();
        Plan finalPlan = plan;
        planItemModels.stream().forEach(planItemModel -> {
            PlanItem planItem = new PlanItem();
            if(planItemModel.getPlanItemId()!=null){
                planItem = planItemRepository.findOne(planItemModel.getPlanItemId());
            }else {
                planItem.setCreatedTimestamp(new Date());
                planItem.setStoreId(store.getMerchantId());
            }
            verifyAppSetting(planItemModel.getApplySetting());
            BeanUtils.copyProperties(planItemModel,planItem);
            ProductSku sku = skuRepository.findOne(planItemModel.getSkuId());
            Assert.notNull(sku,"Sku不能为空");
            planItem.setProduct(sku.getProduct());
            planItem.setProductSku(sku);
            planItem.setApplySetting(planItemModel.getApplySetting());
            planItem.setUpdatedTimestamp(new Date());
            planItem.setUpdatedBy(user.getAdminName());
            planItem.setPlan(finalPlan);
            Assert.notNull(planItemRepository.save(planItem),"保存申领活动项失败");
        });

    }

    @Override
    public Page findPage(Long storeId, String storeIds, Integer page, Integer size,String planType, Date start, Date end, String status) {

        Specification<Plan> specification = new Specification<Plan>() {
            /**
             * Creates a WHERE clause for a query of the referenced entity in form of a {@link Predicate} for the given
             * {@link Root} and {@link CriteriaQuery}.
             *
             * @param root
             * @param query
             * @param cb
             * @return a {@link Predicate}, may be {@literal null}.
             */
            @Override
            public Predicate toPredicate(Root<Plan> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

                List<Predicate> predicates = new ArrayList<>();
                predicates.add(cb.isFalse(root.get("archived")));
                if(StringUtils.isNotBlank(planType)){
                    predicates.add(cb.equal(root.get("planType"),getPlanType(planType)));
                }
                if(StringUtils.isNotBlank(status)){
                    predicates.add(cb.equal(root.get("status"),getPlanStatus(status)));
                }
                if (start!=null){
                    predicates.add(cb.lessThanOrEqualTo(root.get("activeStartTime"),start));

                } else if(end!=null){
                    predicates.add(cb.greaterThanOrEqualTo(root.get("activeEndTime"),end));
                }

                if(StringUtils.isNotBlank(storeIds)){
                    CriteriaBuilder.In<Long> in = cb.in(root.get("storeId"));
                    for (String s : storeIds.split(",")) {
                        in.value(Long.valueOf(s));
                    }
                    predicates.add(in);
                }else{
                    predicates.add(cb.equal(root.get("storeId"), storeId));
                }

                return cb.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };

        Page<Plan> plans = planRepository.findAll(specification, new PageRequest(page - 1, size,new Sort(Sort.Direction.DESC,"updatedTimestamp")));
        Assert.notEmpty(plans.getContent(),"没有数据");

        return plans;
    }

    private PlanType getPlanType(String type) {
        for (PlanType planType : PlanType.values()) {
            if(type.equalsIgnoreCase(planType.name())){
                return planType;
            }
        }
        return null;
    }

    private PlanStatus getPlanStatus(String status) {
        for (PlanStatus planStatus : PlanStatus.values()) {
            if(status.equalsIgnoreCase(planStatus.name())){
                return planStatus;
            }
        }
        return null;
    }


    @Override
    public Plan findOne(Long planId) {
        return planRepository.findOne(planId);
    }

    @Override
    public PlanModel detail(Long planId) {
        Plan plan = planRepository.findOne(planId);
        PlanModel planModel = new PlanModel();
        BeanUtils.copyProperties(plan, planModel);
        planModel.setPlanType(plan.getPlanType());
        ArrayList<PlanItemModel> planItems = new ArrayList<>();
        plan.getPlanItems().forEach(planItem -> {
            PlanItemModel item = new PlanItemModel();
            BeanUtils.copyProperties(planItem,item);
            planItem.setApplySetting(planItem.getApplySetting());
            ProductSku sku = planItem.getProductSku();
            item.setSkuId(sku.getSkuId());
            item.setSkuName(sku.getSkuName());
            planItems.add(item);
        });
        planModel.setPlanItemModels(planItems);
        return planModel;
    }

    @Override
    public PageVO itemDetail(Long planItemId, Integer page, Integer size, String status) {
        PlanItem planItem = planItemRepository.findOne(planItemId);
        PageRequest request = new PageRequest(page - 1, size);
        Page<PlanCusomer> cusomerPage;
        if(StringUtils.isNotBlank(status)){
            cusomerPage = planCustomerRepository.findAllByPlanItemAndStatus(planItem,status,request);
        }else {
            cusomerPage = planCustomerRepository.findAllByPlanItem(planItem,request);
        }
        Assert.notNull(cusomerPage.getContent(),"没有数据");
        ArrayList<Map> arrayList = new ArrayList<>();
        cusomerPage.getContent().forEach(planCusomer -> {
            arrayList.add(convertModel(planCusomer));
        });
        return new PageVO<Map>(page,size,(int)cusomerPage.getTotalElements(),arrayList);

    }

    @Override
    public List planDetail(Long planId, Customer customer) {
        Plan plan = planRepository.findOne(planId);
        Assert.notNull(plan,"找不到该活动");

        ArrayList<PlanItemModel> list = new ArrayList<>();
        plan.getPlanItems().forEach(planItem -> {
            list.add(convertModel(planItem,customer));
        });
        return list;
    }

    /**
     * 中奖
     *
     * @param planCustomerId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void hitLottery(Long planCustomerId , User user) {
        PlanCusomer planCusomer = planCustomerRepository.findOne(planCustomerId);
        PlanItem planItem = planCusomer.getPlanItem();
        Plan plan = planItem.getPlan();

        Assert.isTrue(planCusomer.getStatus().equalsIgnoreCase("wait"),"该用户已经申请过了");
        verifyPlanItem(planCusomer.getApplyQuantity(), planItem, plan);
        int i =0;
        Assert.isTrue((i = planItemRepository.hitCustomer(planItem.getPlanItemId(),planCusomer.getApplyQuantity()))>0,"申请失败");
        planCusomer.setStatus("hit");
        planCusomer.setUpdatedTimestamp(new Date());
        planCusomer.setUpdatedBy(user.getAdminName());
        planCusomer = planCustomerRepository.save(planCusomer);

        plan.setStatus(PlanStatus.RUNNING);
        if(planItemRepository.quantity(plan.getPlanId())==0){
            plan.setStatus(PlanStatus.END);
        }
        plan = planRepository.save(plan);

    }

    private void verifyPlanItem(Integer applyQuantity, PlanItem planItem, Plan plan) {
        Assert.isTrue(!plan.getArchived(),"该活动不存在");
        LocalDateTime start = LocalDateUtils.dateTimeFromDate(plan.getActiveStartTime());
        LocalDateTime end = LocalDateUtils.dateTimeFromDate(plan.getActiveEndTime());
        LocalDateTime now = LocalDateTime.now();
        if(now.isBefore(start)||now.isAfter(end)){
            throw new KnownRuntimeException("不在活动有效期内");
        }

        if(planItem.getHasLimit()){
            Integer quantity = planItem.getLimitQuantity();
            Assert.isTrue(applyQuantity<=quantity,String.format("用户达到领取上限，上限次数（%s） 本次领取（%s）",quantity,applyQuantity));
        }
        if(planItem.getInventoryAvailable()<applyQuantity){
            throw new KnownRuntimeException("库存不足");
        }
    }

    @Override
    public List<Map> customerDetail(Customer customer ,String status) {
        List<PlanCusomer> planCusomers;
        if(StringUtils.isBlank(status)){
            planCusomers = planCustomerRepository.findAllByCustomer(customer);
        }else {
            planCusomers = planCustomerRepository.findAllByCustomerAndStatus(customer.getCustomerId(),status,new Date());
            if(status.equalsIgnoreCase("hit")){
                planCusomers.addAll(planCustomerRepository.findAllByCustomerNotIsActive(customer.getCustomerId(),new Date()));
            }
        }

        ArrayList<Map> maps = new ArrayList<>();
        if(!planCusomers.isEmpty()){

            planCusomers.stream().forEach(planCusomer -> {
                //查询我的申领未开奖活动  过滤掉已结束活动
                if(status.equalsIgnoreCase("wait")&&!isActive(planCusomer.getPlanItem().getPlan())){
                    return;
                };
                HashMap<String, Object> map = new HashMap<>();
                PlanItem planItem = planCusomer.getPlanItem();
                ProductSku sku = planItem.getProductSku();
                Plan plan = planItem.getPlan();
                map.put("skuId",sku.getSkuId());
                map.put("skuName",sku.getSkuName());
                Media media = skuMediaMapService.getDefaultPrimaryMedia(sku.getProduct().getDefaultSku());
                map.put("img",media==null?"": HttpRequestUtils.getUrl(media.getUrl()));
                map.put("activeStartTime",plan.getActiveStartTime());
                map.put("activeEndTime",plan.getActiveEndTime());
                map.put("applyDate",planCusomer.getCreatedTimestamp());
                map.put("hitDate",planCusomer.getUpdatedTimestamp());
                map.put("activeEndTime",plan.getActiveEndTime());
                map.put("status", planCusomer.getStatus());
                map.put("shipStatus", planCusomer.getShippingStatus());
                map.put("feedback", planCusomer.getCustomerFeedback());
                map.put("planCustomerId", planCusomer.getPlancusomerId());
                maps.add(map);
            });
        }
        Assert.notEmpty(maps,"没有数据");
        return maps;
    }

    private boolean isActive(Plan plan) {
        LocalDateTime start = LocalDateUtils.dateTimeFromDate(plan.getActiveStartTime());
        LocalDateTime end = LocalDateUtils.dateTimeFromDate(plan.getActiveEndTime());
        return LocalDateTime.now().isAfter(start)&&LocalDateTime.now().isBefore(end);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveFeedback(String feedbackData, Long planCustomerId) {
        // 校验反馈结果正确性
        Map<String, List<String>> data = verifyFeedback(feedbackData);
        Assert.notNull(data.get("feedback"),"反馈数据不能为空");
        Assert.notNull(data,"数据格式不正确");
        PlanCusomer planCusomer = planCustomerRepository.findOne(planCustomerId);
        Assert.notNull(planCusomer,"该申请数据不存在");

        planCusomer.setCustomerFeedback(feedbackData);
        Assert.notNull(planCustomerRepository.save(planCusomer),"反馈失败");
    }

    @Override
    public PageVO<Map> feedbacks(Long planItemId, Integer page, Integer size) {

        PlanItem item = planItemRepository.findOne(planItemId);
        PageRequest pageRequest = new PageRequest(page - 1, size, Sort.Direction.DESC, "updatedTimestamp");
        List<PlanCusomer> cusomerList = planCustomerRepository.findAllByPlanItemAndStatusAndCustomerFeedbackIsNotNull(item, "hit",pageRequest);
        Assert.notEmpty(cusomerList,"没有反馈记录");

        List<HashMap<String, Object>> mapList = cusomerList.stream().map(planCusomer -> {
            HashMap<String, Object> map = new HashMap<>();
            map.put("planCustomerId", planCusomer.getPlancusomerId());
            map.put("customerName", planCusomer.getCustomerName());
            map.put("customerPhone", planCusomer.getCustomerPhone());
            map.put("customerFeedback", planCusomer.getCustomerFeedback());
            map.put("customerId", planCusomer.getCustomer().getCustomerId());
            map.put("customerApply", planCusomer.getCustomerApply());
            return map;
        }).collect(Collectors.toList());
        return new PageVO<Map>(page,size,cusomerList.size(),mapList);
    }

    @Override
    public List winners(Long planItemId) {

        PlanItem planItem = planItemRepository.findOne(planItemId);
        Plan plan = planItem.getPlan();
        Assert.isTrue(plan.getStatus().equals(PlanStatus.END.name()),"活动还未结束..不能开奖");
        List<PlanCusomer> cusomers = planCustomerRepository.findAllByPlanItemAndStatus(planItem, "hit");
        Assert.notEmpty(cusomers,"找不到获奖名单");

        return cusomers.stream().map(planCusomer -> {
            HashMap<String, Object> map = new HashMap<>();
            map.put("customerName",planCusomer.getCustomerName());
            map.put("customerId",planCusomer.getCustomer().getCustomerId());
            String phone = planCusomer.getCustomerPhone();
            // 转换4-7位为*号
            if(StringUtils.isNotBlank(phone)){
                phone = phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
            }
            map.put("customerPhone",phone);
            return map;
        }).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void commitApply(PlanCommitModel commitModel, Customer customer) {

        PlanItem item = planItemRepository.findOne(commitModel.getPlanItemId());
        PlanCusomer planCusomer = planCustomerRepository.findFirstByPlanItemAndCustomer(item,customer);
        Assert.isNull(planCusomer,"请勿重复申领该活动商品");

        verifyPlanItem(commitModel.getApplyQuantity(), item, item.getPlan());

        planCusomer = new PlanCusomer();
        BeanUtils.copyProperties(commitModel,planCusomer);
        planCusomer.setCustomer(customer);
        Gson gson = new Gson();
        try {
            gson.fromJson(commitModel.getCustomerApply(),new TypeToken<List<Map<String,List>>>(){}.getType());
        } catch (JsonSyntaxException e) {
            log.warn("用户试用商品申请 ， 数据录入错误， 用户姓名：{%s} 数据：{%s}",customer.getCustomerNickname(),commitModel.getCustomerApply());
            throw new KnownRuntimeException("用户录入的数据格式不匹配");
        }
        PlanItem planItem = planItemRepository.findOne(commitModel.getPlanItemId());
        Assert.notNull(planItem,"活动不存在");
        planCusomer.setPlanItem(planItem);
        planCusomer.setPlanId(planItem.getPlan().getPlanId());
        planCusomer.setProduct(planItem.getProduct());
        planCusomer.setProductSku(planItem.getProductSku());
        planCusomer.setStatus("wait");
        planCusomer.setShippingStatus("wait");
        planCusomer.setCreatedTimestamp(new Date());
        planCusomer.setUpdatedTimestamp(new Date());
        Assert.notNull(planCusomer = planCustomerRepository.save(planCusomer),"申请失败");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long planId, MerchantStore merchantStore, User user) {
        Plan plan = planRepository.findOne(planId);
        if(plan.getStatus().equals(PlanStatus.RUNNING)){
            throw new KnownRuntimeException("活动已经在进行..不能删除");
        }
        plan.setArchived(true);
        plan.setUpdatedBy(user.getAdminName());

    }

    private PlanItemModel convertModel(PlanItem planItem, Customer customer) {
        PlanItemModel itemRo = new PlanItemModel();
        BeanUtils.copyProperties(planItem,itemRo);
        ProductSku defaultSku = planItem.getProduct().getDefaultSku();
        itemRo.setSkuName(planItem.getProductSku().getSkuName());
        itemRo.setApplySetting(planItem.getApplySetting());
        itemRo.setActiveStartTime(planItem.getPlan().getActiveStartTime());
        itemRo.setActiveEndTime(planItem.getPlan().getActiveEndTime());
        if(customer!=null&&planCustomerRepository.findFirstByPlanItemAndCustomer(planItem,customer)!=null){
            itemRo.setIsApply(Boolean.TRUE);
        }
        //设置主图片
        List<Media> primaryMedia = skuMediaMapService.getPrimaryMedia(defaultSku);
        if(null!=primaryMedia&&!primaryMedia.isEmpty()){
            itemRo.setHeadImages(
                    primaryMedia.stream().map(media -> {
                        return HttpRequestUtils.getUrl(media.getUrl());
                    }).collect(Collectors.toList())
            );
        }
        //设置详情图片
        List<Media> descriptionMedia = skuMediaMapService.getDescriptionMedia(defaultSku);
        if(null!=descriptionMedia&&!descriptionMedia.isEmpty()){
            itemRo.setDescriptionImages(
                    descriptionMedia.stream().map(media -> {
                        return HttpRequestUtils.getUrl(media.getUrl());
                    }).collect(Collectors.toList())
            );
        }

        Gson gson = new Gson();
        itemRo.setApplySettingFormat(verifyAppSetting(itemRo.getApplySetting()));
        List<PlanCusomer> cusomers = planItem.getPlanCusomers();
        //申请 --申请成功人数
        cusomers.stream().forEach(planCusomer -> {
            if(planCusomer.getStatus().equalsIgnoreCase("wait")){
                itemRo.setWaitTotal(itemRo.getWaitTotal()==null?1:itemRo.getWaitTotal()+1);
            }else {
                itemRo.setHitTotal(itemRo.getHitTotal()==null?1:itemRo.getHitTotal()+1);
            }
        });
        return itemRo;
    }

    private Map convertModel(PlanCusomer planCusomer) {
        HashMap<String, Object> map = new HashMap<>(6);
        map.put("status",planCusomer.getStatus());
        map.put("planCustomerId",planCusomer.getPlancusomerId());
        map.put("apply",planCusomer.getCustomerApply());
        map.put("phone",planCusomer.getCustomerPhone());
        map.put("name",planCusomer.getCustomerName());
        map.put("remark",planCusomer.getCustomerRemark());
        map.put("customerId",planCusomer.getCustomer().getCustomerId());
        map.put("gender",planCusomer.getCustomer().getCustomerGender());
        map.put("applyQuantity",planCusomer.getApplyQuantity());
        return map;
    }

    private List verifyAppSetting(String applySetting) {
        Gson gson = new Gson();
        try {
            List<Map<String,List<String>>> mapList = gson.fromJson(applySetting, new TypeToken<List<Map<String, List<String>>>>() {
            }.getType());
            return mapList;
        } catch (JsonSyntaxException e) {
            throw new KnownRuntimeException("应用设置参数不正确..[{key:[value1,value2]},{},{}]");
        }
    }

    private Map<String, List<String>> verifyFeedback(String feedback) {
        Gson gson = new Gson();
        try {
            Map<String,List<String>> map = gson.fromJson(feedback, new TypeToken<Map<String, List<String>>>() {
            }.getType());
            return map;
        } catch (JsonSyntaxException e) {
            throw new KnownRuntimeException("应用设置参数不正确..[{key:[value1,value2]},{},{}]");
        }
    }

}
